﻿using System;
using System.Diagnostics;
using System.Linq;
using System.Timers;

namespace V8.Net
{
    public class Program
    {
        static V8Engine _JSServer;

        static Timer _TitleUpdateTimer;

        static void Main(string[] args)
        {

            try
            {
                Console.Title = "V8.Net Console";

                Console.Write(Environment.NewLine + "创建一个V8Engine实例 ...");
                _JSServer = new V8Engine();
                Console.WriteLine(" 完成!");

                Console.Write("测试兼容性...");
                _JSServer.RunMarshallingTests();
                Console.WriteLine(" 通过!");

                _TitleUpdateTimer = new Timer(500);
                _TitleUpdateTimer.AutoReset = true;
                _TitleUpdateTimer.Elapsed += (_o, _e) =>
                {
                    Console.Title = "V8.Net 控制台 - " + (IntPtr.Size == 4 ? "32-bit" : "64-bit") + " 模式 (处理: " + _JSServer.TotalHandles
                        + " / 待处理的本机GC: " + _JSServer.TotalHandlesPendingDisposal
                        + " / 已缓存: " + _JSServer.TotalHandlesCached
                        + " / 正在使用: " + (_JSServer.TotalHandles - _JSServer.TotalHandlesCached) + ")";
                };
                _TitleUpdateTimer.Start();

                _JSServer.WithContextScope = () =>
                {
                    Console.Write(Environment.NewLine + "创建全局CLR类型 ...");
                    // （注意：不需要显式注册类型，但是建议您这样做以提高效率。）
                    _JSServer.RegisterType(typeof(Object), "Object", true, ScriptMemberSecurity.Locked);
                    _JSServer.RegisterType(typeof(Type), "Type", true, ScriptMemberSecurity.Locked);
                    _JSServer.RegisterType(typeof(String), "String", true, ScriptMemberSecurity.Locked);
                    _JSServer.RegisterType(typeof(Boolean), "Boolean", true, ScriptMemberSecurity.Locked);
                    _JSServer.RegisterType(typeof(Array), "Array", true, ScriptMemberSecurity.Locked);
                    _JSServer.RegisterType(typeof(System.Collections.ArrayList), null, true, ScriptMemberSecurity.Locked);
                    _JSServer.RegisterType(typeof(char), null, true, ScriptMemberSecurity.Locked);
                    _JSServer.RegisterType(typeof(int), null, true, ScriptMemberSecurity.Locked);
                    _JSServer.RegisterType(typeof(Int16), null, true, ScriptMemberSecurity.Locked);
                    _JSServer.RegisterType(typeof(Int32), null, true, ScriptMemberSecurity.Locked);
                    _JSServer.RegisterType(typeof(Int64), null, true, ScriptMemberSecurity.Locked);
                    _JSServer.RegisterType(typeof(UInt16), null, true, ScriptMemberSecurity.Locked);
                    _JSServer.RegisterType(typeof(UInt32), null, true, ScriptMemberSecurity.Locked);
                    _JSServer.RegisterType(typeof(UInt64), null, true, ScriptMemberSecurity.Locked);
                    _JSServer.RegisterType(typeof(Enumerable), null, true, ScriptMemberSecurity.Locked);
                    _JSServer.RegisterType(typeof(System.IO.File), null, true, ScriptMemberSecurity.Locked);

                    var hSystem = _JSServer.CreateObject();
                    _JSServer.DynamicGlobalObject.System = hSystem;
                    hSystem.SetProperty(typeof(Object)); // （注意：未使用可选参数，因此这将仅查找并应用上面现有的已注册类型详细信息。）
                    hSystem.SetProperty(typeof(String));
                    hSystem.SetProperty(typeof(Boolean));
                    hSystem.SetProperty(typeof(Array));
                    _JSServer.GlobalObject.SetProperty(typeof(Type));
                    _JSServer.GlobalObject.SetProperty(typeof(System.Collections.ArrayList));
                    _JSServer.GlobalObject.SetProperty(typeof(char));
                    _JSServer.GlobalObject.SetProperty(typeof(int));
                    _JSServer.GlobalObject.SetProperty(typeof(Int16));
                    _JSServer.GlobalObject.SetProperty(typeof(Int32));
                    _JSServer.GlobalObject.SetProperty(typeof(Int64));
                    _JSServer.GlobalObject.SetProperty(typeof(UInt16));
                    _JSServer.GlobalObject.SetProperty(typeof(UInt32));
                    _JSServer.GlobalObject.SetProperty(typeof(UInt64));
                    _JSServer.GlobalObject.SetProperty(typeof(Enumerable));
                    _JSServer.GlobalObject.SetProperty(typeof(Environment));
                    _JSServer.GlobalObject.SetProperty(typeof(System.IO.File));

                    _JSServer.GlobalObject.SetProperty(typeof(Uri), null, true, ScriptMemberSecurity.Locked); // （注意：尚未注册，但会自动注册！）
                    Console.WriteLine(" 通过!");

                    _JSServer.ConsoleExecute(@"dump = function(o) { var s=''; if (typeof(o)=='undefined') return 'undefined';"
                        + @" if (typeof o.valueOf=='undefined') return ""'valueOf()' '"" +(typeof o)+""'上缺少-如果您要继承V8ManagedObject,请确保您没有阻塞该属性."";"
                        + @" if (typeof o.toString=='undefined') return ""'toString()' is missing on '""+o.valueOf()+""' - if you are inheriting from V8ManagedObject, make sure you are not blocking the property."";"
                        + @" for (var p in o) {var ov='', pv=''; try{ov=o.valueOf();}catch(e){ov='{error: '+e.message+': '+dump(o)+'}';} try{pv=o[p];}catch(e){pv=e.message;} s+='* '+ov+'.'+p+' = ('+pv+')\r\n'; } return s; }");

                    _JSServer.GlobalObject.SetProperty(typeof(Console), null, true, ScriptMemberSecurity.Locked);//创建一个全局的'Console'对象
                    _JSServer.GetTypeBinder(typeof(String)).ChangeMemberSecurity("Empty", ScriptMemberSecurity.NoAcccess);//将'System.String.Empty'成员安全属性更改为'NoAccess'的示例

                    _JSServer.Execute(System.IO.File.ReadAllText(@"E:\C工作\当前香港\abck\AKAMAI-HTTP2\bin\test.js"));
                    Console.WriteLine(Environment.NewLine + "这是调用和传递CLR方法/类型的人为例子 ...");
                    _JSServer.VerboseConsoleExecute(@"r = Enumerable.Range(1,Int32('10'));");
                    _JSServer.VerboseConsoleExecute(@"a = System.String.Join$1([Int32], ', ', r);");
                };

                Console.WriteLine(Environment.NewLine + @"准备 -只需输入脚本即可执行。 键入'\'或'\help'以获取控制台特定命令的列表.");

                string input, lcInput;

                while (true)
                {
                    try
                    {
                        Console.Write(Environment.NewLine + "> ");

                        input = Console.ReadLine();
                        lcInput = input.Trim().ToLower();
                        if (lcInput == @"\help" || lcInput == @"\")
                        {
                            Console.WriteLine(@"特殊的控制台命令(所有命令均通过前面的'\'字符触发,以免将其与脚本代码混淆):");
                            Console.WriteLine(@"\gc - 触发垃圾收集(出于测试目的).");
                            Console.WriteLine(@"\v8gc - 在V8中触发垃圾收集(出于测试目的).");
                            Console.WriteLine(@"\gctest - 针对V8.NET和本机V8引擎运行简单的GC测试.");
                            Console.WriteLine(@"\speedtest - 运行一个简单的测试脚本以使用V8引擎测试V8.NET性能.");
                            Console.WriteLine(@"\exit - 退出控制台.");
                            continue;
                        }
                        else if (lcInput == @"\gc")
                        {
                            Console.Write("\r\nForcing garbage collection ... ");
                            GC.Collect();
                            GC.WaitForPendingFinalizers();
                            Console.WriteLine("Done.\r\n");
                        }
                        else if (lcInput == @"\v8gc")
                        {
                            Console.Write("\r\n强制V8垃圾回收 ... ");
                            _JSServer.WithContextScope = () =>
                            {
                                _JSServer.ForceV8GarbageCollection();
                            };
                            Console.WriteLine("完成.\r\n");
                        }
                        else if (lcInput == @"\gctest")
                        {
                            Console.WriteLine("\r\n测试垃圾收集 ... ");

                            V8NativeObject tempObj;
                            InternalHandle internalHandle = InternalHandle.Empty;
                            int i;

                            _JSServer.WithContextScope = () =>
                            {
                                Console.WriteLine("将'this.tempObj'设置为新的托管对象 ...");

                                tempObj = _JSServer.CreateObject<V8NativeObject>();
                                internalHandle = tempObj.Handle;
                                Handle testHandle = internalHandle;
                                _JSServer.DynamicGlobalObject.tempObj = tempObj;

                                // ... because we have a strong reference to the handle, the managed and native objects are safe; however,
                                // this block has the only strong reference, so once the reference goes out of scope, the managed GC will attempt to
                                // collect it, which will mark the handle as ready for collection (but it will not be destroyed just yet) ...

                                Console.WriteLine("清除托管引用并运行垃圾收集器 ...");
                                testHandle = null;

                            };

                            GC.Collect();
                            GC.WaitForPendingFinalizers();
                            // (we wait for the 'testHandle' handle object to be collected, which will dispose the handle)
                            // (note: we do not call 'Set()' on 'internalHandle' because the "Handle" type takes care of the disposal)

                            for (i = 0; i < 3000 && internalHandle.ReferenceCount > 1; i++)
                                System.Threading.Thread.Sleep(1); // (just wait for the worker)

                            if (internalHandle.ReferenceCount > 1)
                                throw new Exception("句柄仍未准备好进行GC ...出了点问题.");

                            Console.WriteLine("成功！ 托管句柄实例正在处理中.");
                            Console.WriteLine("接下来清除句柄对象引用 ...");

                            // ... because we still have a reference to 'tempObj' at this point, the managed and native objects are safe (and the handle
                            // above!); however, this block has the only strong reference keeping everything alive (including the handle), so once the
                            // reference goes out of scope, the managed GC will collect it, which will mark the managed object as ready for collection.
                            // Once both the managed object and handle are marked, this in turn marks the native handle as weak. When the native V8
                            // engine's garbage collector is ready to dispose of the handle, as call back is triggered and the the native object and
                            // handles will finally be removed ...

                            tempObj = null;

                            Console.WriteLine("强制CLR垃圾回收 ... ");
                            GC.Collect();
                            GC.WaitForPendingFinalizers();

                            Console.WriteLine("等待工作人员在本机V8一侧使对象变弱 ... ");

                            for (i = 0; i < 6000 && !internalHandle.IsNativelyWeak; i++)
                                System.Threading.Thread.Sleep(1);

                            if (!internalHandle.IsNativelyWeak)
                                throw new Exception("对象还不弱...出了点问题.");


                            Console.WriteLine("强制V8垃圾回收 ... ");
                            _JSServer.WithContextScope = () => { _JSServer.DynamicGlobalObject.tempObj = null; };
                            for (i = 0; i < 3000 && !internalHandle.IsDisposed; i++)
                            {
                                _JSServer.WithContextScope = _JSServer.ForceV8GarbageCollection;
                                System.Threading.Thread.Sleep(1);
                            }

                            Console.WriteLine("寻找对象 ...");

                            if (!internalHandle.IsDisposed) throw new Exception("托管对象未垃圾回收.");
                            // (note: this call is only valid as long as no more objects are created before this point)
                            Console.WriteLine("成功！ 托管的V8NativeObject实例已处置.");
                            Console.WriteLine("\r\n完成.\r\n");
                        }
                        else if (lcInput == @"\speedtest")
                        {
                            var timer = new Stopwatch();
                            long startTime, elapsed;
                            long count;
                            double result1, result2, result3, result4;

                            Console.WriteLine(Environment.NewLine + "运行速度测试 ... ");

                            _JSServer.WithContextScope = () =>
                            {
                                timer.Start();

                                //??Console.WriteLine(Environment.NewLine + "Running the property access speed tests ... ");
                                Console.WriteLine("(注意:'V8NativeObject'对象始终比使用'V8ManagedObject'对象快,因为本机对象将值存储在V8引擎内,而托管对象将其值存储在.NET端.");

                                count = 200000000;

                                Console.WriteLine("\r\n测试全局属性写入速度 ... ");
                                startTime = timer.ElapsedMilliseconds;
                                _JSServer.Execute("o={i:0}; for (o.i=0; o.i<" + count + "; o.i++) n = 0;"); // (o={i:0}; is used in case the global object is managed, which will greatly slow down the loop)
                                elapsed = timer.ElapsedMilliseconds - startTime;
                                result1 = (double)elapsed / count;
                                Console.WriteLine(count + " 循环 @ " + elapsed + "毫秒 总 = " + result1.ToString("0.0#########") + "每次通过毫秒.");

                                Console.WriteLine("\r\n测试全局属性读取速度 ... ");
                                startTime = timer.ElapsedMilliseconds;
                                _JSServer.Execute("for (o.i=0; o.i<" + count + "; o.i++) n;");
                                elapsed = timer.ElapsedMilliseconds - startTime;
                                result2 = (double)elapsed / count;
                                Console.WriteLine(count + " 循环 @ " + elapsed + "毫秒 总 = " + result2.ToString("0.0#########") + " 每次通过毫秒.");

                                count = 200000;

                                Console.WriteLine("\r\n测试托管对象（带有拦截器）上的属性写入速度 ... ");
                                _JSServer.DynamicGlobalObject.mo = _JSServer.CreateObjectTemplate().CreateObject();
                                startTime = timer.ElapsedMilliseconds;
                                _JSServer.Execute("o={i:0}; for (o.i=0; o.i<" + count + "; o.i++) mo.n = 0;");
                                elapsed = timer.ElapsedMilliseconds - startTime;
                                result3 = (double)elapsed / count;
                                Console.WriteLine(count + " 循环 @ " + elapsed + "毫秒 总 = " + result3.ToString("0.0#########") + " 每次通过毫秒.");

                                Console.WriteLine("\r\n测试托管对象（带有拦截器）上的属性读取速度 ... ");
                                startTime = timer.ElapsedMilliseconds;
                                _JSServer.Execute("for (o.i=0; o.i<" + count + "; o.i++) mo.n;");
                                elapsed = timer.ElapsedMilliseconds - startTime;
                                result4 = (double)elapsed / count;
                                Console.WriteLine(count + " loops @ " + elapsed + "ms total = " + result4.ToString("0.0#########") + " ms each pass.");

                                Console.WriteLine("\r\n更新本机属性比托管属性快{0:N2}x", result3 / result1);
                                Console.WriteLine("\r\n读取本机属性比托管属性快{0:N2}x", result4 / result2);
                            };
                            Console.WriteLine("\r\n完成.\r\n");
                        }
                        else if (lcInput == @"\exit")
                        {
                            Console.WriteLine("退出.");
                            break;
                        }
                        else
                        {
                            _JSServer.WithContextScope = () =>
                            {
                                Console.WriteLine();

                                try
                                {
                                    var result = _JSServer.Execute(input, "V8.NET Console");
                                    Console.WriteLine(result.AsString);
                                }
                                catch (Exception ex)
                                {
                                    Console.WriteLine();
                                    Console.WriteLine();
                                    Console.WriteLine(Exceptions.GetFullErrorMessage(ex));
                                    Console.WriteLine();
                                    Console.WriteLine("Error!  Press any key to continue ...");
                                    Console.ReadKey();
                                }
                            };
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine();
                        Console.WriteLine();
                        Console.WriteLine(Exceptions.GetFullErrorMessage(ex));
                        Console.WriteLine();
                        Console.WriteLine("Error!  Press any key to continue ...");
                        Console.ReadKey();
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine(Exceptions.GetFullErrorMessage(ex));
                Console.WriteLine();
                Console.WriteLine("Error!  Press any key to exit ...");
                Console.ReadKey();
            }
        }
    }
}